﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace Duellum.Wpf2d.Plugin
{
	public class MapVisualHost : FrameworkElement
	{
		static public readonly RoutedEvent MouseClickEvent
			= EventManager.RegisterRoutedEvent(
				"MouseClick",
				RoutingStrategy.Bubble,
				typeof(MouseButtonEventHandler),
				typeof(MapScrollViewer)
			);
		public event MouseButtonEventHandler MouseClick
		{
			add { AddHandler(MouseClickEvent, value); } 
			remove { RemoveHandler(MouseClickEvent, value); }
		}

		static MapVisualHost()
		{
		    MapScrollViewer.MapProperty.OverrideMetadata(
		        typeof(MapVisualHost),
		        new FrameworkPropertyMetadata(
					null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits
				)
		    );
			MapScrollViewer.LevelProperty.OverrideMetadata(
			    typeof(MapVisualHost),
			    new FrameworkPropertyMetadata(
			        0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits
			    )
			);
		    MapScrollViewer.ZoomProperty.OverrideMetadata(
		        typeof(MapVisualHost),
		        new FrameworkPropertyMetadata(
		            1d,
		            FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.Inherits
		        )
		    );
		}

		private ZSortedVisualCollection visualChildren;
		private ICollection<IMapRenderer> renderers;

		public MapVisualHost() : base()
		{
			ToolTipService.SetInitialShowDelay(this, 500);
			ToolTipService.SetShowDuration(this, 9000);
			ToolTipService.SetBetweenShowDelay(this, 500);
			this.ToolTip = new ToolTip(){ Content = "-" };
			
			visualChildren = new ZSortedVisualCollection(this);
            renderers = new List<IMapRenderer>();
		}

		//public DuelMap Map	{ get { return (DuelMap)GetValue(MapScrollViewer.MapProperty); } }
		//public int Level	{ get { return (int)GetValue(MapScrollViewer.LevelProperty); } }
		//public double Zoom	{ get { return (double)GetValue(MapScrollViewer.ZoomProperty); } }

		// Provide a required override for the VisualChildrenCount property.
		protected override int VisualChildrenCount
		{
			get { return visualChildren.Count; }
		}

		// Provide a required override for the GetVisualChild method.
		protected override Visual GetVisualChild(int index)
		{
			if (index < 0 || index >= visualChildren.Count) throw new ArgumentOutOfRangeException();
			return visualChildren[index];
		}
		
		protected override void OnPreviewMouseMove(MouseEventArgs e)
		{
			InvalidateToolTip();
		    base.OnPreviewMouseMove(e);
		}
		
		protected override void OnMouseDown(MouseButtonEventArgs e)
		{
			base.CaptureMouse();
			
			base.OnMouseDown(e);
		}

		protected override void OnMouseUp(MouseButtonEventArgs e)
		{
			base.OnMouseUp(e);
			
			if (this.IsMouseCaptured) {
				this.OnMouseClick(e);
				this.ReleaseMouseCapture();
			}
		}

		protected virtual void OnMouseClick(MouseButtonEventArgs sourceE)
		{
			var e = new MouseButtonEventArgs(sourceE.MouseDevice, sourceE.Timestamp, sourceE.ChangedButton, sourceE.StylusDevice);
			e.RoutedEvent = MouseClickEvent;
			base.RaiseEvent(e);
		}
		
		public void InvalidateToolTip()
		{
		    var tt = (ToolTip)this.ToolTip;
		    if (tt != null) {
		        if (tt.IsOpen) tt.IsOpen = false;
		        
	            //move mouse out of this control artificially
	            PopupControlServiceInterop.Current.OnMouseMove(null, default(Point));
	            PopupControlServiceInterop.Current.OnMouseMove(this, default(Point));
		    }
		}
		
		internal void AddRenderer(IMapRenderer renderer)
		{
			renderer.VisualHost = this;

			foreach (var v in renderer.Visuals) visualChildren.Add(v);
			
			renderer.VisualAdded	+= ((r, v) => visualChildren.Add(v));
			renderer.VisualRemoved	+= ((r, v) => visualChildren.Remove(v));
			renderers.Add(renderer);
		}

		internal void RemoveRenderer(IMapRenderer renderer)
		{
			if (renderers.Remove(renderer)) {
				foreach (var v in renderer.Visuals) visualChildren.Remove(v);
			}
		}

		protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
		{
		    return new PointHitTestResult(this, hitTestParameters.HitPoint);
		    //return base.HitTestCore(hitTestParameters);
		}

		protected override void OnRender(DrawingContext drawingContext)
		{
			this.InvalidateToolTip();
			
            foreach (var r in renderers) { r.OnRender(); }
            
			base.OnRender(drawingContext);
		}

		protected override Size MeasureOverride(Size constraint)
		{
		    var size = renderers.Select(r => r.GetMeasure()).Aggregate((Size?)null, JoinSizes);
		    return size ?? default(Size);
		}

	    private static Size? JoinSizes(Size? s1, Size? s2)
	    {
			if (s1 == null) return s2;
			if (s2 == null) return s1;
	        return new Size(
				Math.Max(s1.Value.Width, s2.Value.Width),
				Math.Max(s1.Value.Height, s2.Value.Height)
			);
	    }
	}
}
